/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd.sessioner;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.zhiqim.httpd.HttpSession;
import org.zhiqim.httpd.HttpSessionAbs;
import org.zhiqim.httpd.HttpSessionManager;
import org.zhiqim.httpd.HttpSessionUser;
import org.zhiqim.kernel.model.maps.HashMapSO;
import org.zhiqim.kernel.model.maps.HashMapSS;
import org.zhiqim.kernel.model.maps.MapSV;
import org.zhiqim.kernel.json.JsonString;
import org.zhiqim.kernel.json.Jsons;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Classes;

/**
 * RMI会话对象，继承自会话抽象类，实现在HashSessionManager中管理
 *
 * @version v1.0.0 @author zouzhigang 2014-3-21 新建与整理
 */
public class RmiSession extends HttpSessionAbs implements HttpSession
{
    /** 空构造函数用于远程时创建再解析 */
    public RmiSession()
    {
        this.attributeMap = new HashMapSO();
    }
    
    /** 参数构造函数用于本地创建传到远程 */
    public RmiSession(String sessionId, String userIp, String userAgent, HttpSessionManager sessionManager)
    {
        super(sessionId, userIp, userAgent, sessionManager);
    }
    
    @Override
    public void flush()
    {
        this.sessionManager.setSession(this);
    }
    
    /******************************************************************************************/
    //RMI会话从远程读取解析的方法 setSessionManager&parse
    /******************************************************************************************/
    
    /** 设置会话管理器 */
    public HttpSessionAbs setSessionManager(HttpSessionManager sessionManager)
    {
        this.sessionManager = sessionManager;
        this.context = sessionManager.getContext();
        return this;
    }
    
    /** 解析JSON字符串 */
    public void parse(String json)
    {
        //1.先读取会话标准五属性
        sessionId = Jsons.getString(json, "sessionId");
        created = Jsons.getLong(json, "created");
        accessed = Jsons.getLong(json, "accessed");
        userIp = Jsons.getString(json, "userIp");
        userAgent = Jsons.getString(json, "userAgent");
        
        //2.再读取会话业务属性，业务属性使用哈希表转数组方式保存为字符串
        String attributeJson = Jsons.getArray(json, "attributeMap");
        List<RmiSessionAttribute> attributeList = Jsons.toList(attributeJson, RmiSessionAttribute.class);
        for (RmiSessionAttribute attribute : attributeList)
        {
            Class<?> cls = Classes.forName(attribute.getCls());
            if (!attribute.hasGenericClass())
            {//没有泛型
                Object val = Jsons.toObject(attribute.getValue(), cls);
                attributeMap.put(attribute.getKey(), val);
                continue;
            }
            
            Class<?> gls = Classes.forName(attribute.getGls());
            if (Classes.isList(cls))
            {//列表
                Object val = Jsons.toList(attribute.getValue(), cls, gls);
                attributeMap.put(attribute.getKey(), val);
                attributeGenerics.put(attribute.getKey(), attribute.getGls());
            }
            else if (Classes.isMapSV(cls))
            {//链表
                Object val = Jsons.toMapSV(attribute.getValue(), cls, gls);
                attributeMap.put(attribute.getKey(), val);
                attributeGenerics.put(attribute.getKey(), attribute.getGls());
            }
            else
            {//其他的不支持
                throw Asserts.exception("RmiSession解析会话业务属性时只支持[List<T>]和[MapSV<V>]两种泛型类型");
            }
        }
        
        //3.最后读取会话用户属性
        String sesionUserStr = Jsons.getString(json, "sessionUser");
        if (sesionUserStr != null)
        {
            //3.1 检查sessionUser类
            String className = Jsons.getString(sesionUserStr, "className");
            Class<?> cls = Classes.forName(className);
            Asserts.as(cls != null?null:"RmiSession解析SessionUser时，找不到类[%s]", className);
            
            //3.2 对sessionUser字段赋值
            sessionUser = (HttpSessionUser)Jsons.toObject(sesionUserStr, cls);
            bindSessionUser(this);
            
            //3.3 对valueMap进行处理
            String valueMapStr = Jsons.getArray(sesionUserStr, "valueMap");
            attributeList = Jsons.toList(valueMapStr, RmiSessionAttribute.class);
            for (RmiSessionAttribute attribute : attributeList)
            {
                cls = Classes.forName(attribute.getCls());
                if (!attribute.hasGenericClass())
                {//没有泛型
                    Object val = Jsons.toObject(attribute.getValue(), cls);
                    setSessionUserValueGeneric(attribute.getKey(), val, null);
                    continue;
                }
                
                Class<?> gls = Classes.forName(attribute.getGls());
                if (Classes.isList(cls))
                {//列表
                    Object val = Jsons.toList(attribute.getValue(), cls, gls);
                    setSessionUserValueGeneric(attribute.getKey(), val, attribute.getGls());
                }
                else if (Classes.isMapSV(cls))
                {//链表
                    Object val = Jsons.toMapSV(attribute.getValue(), cls, gls);
                    setSessionUserValueGeneric(attribute.getKey(), val, attribute.getGls());
                }
                else
                {//其他的不支持
                    throw Asserts.exception("RmiSession解析会话用户属性[valueMap]时只支持[List<T>]和[MapSV<V>]两种泛型类型");
                }
            }
            
            //3.4 对会话用户中的more进行检查，跨系统时不会被解析
            HashMapSS fieldMap = Jsons.toMapSS(sesionUserStr);
            fieldMap.remove("valueMap");
            
            List<Field> fieldList = Classes.getFieldListDeep(cls);
            for (Field field : fieldList)
            {//删除已在字段中的
                fieldMap.remove(field.getName());
            }
            
            for (String key : fieldMap.keySet())
            {//剩余的不在字段中的，放置到moreMap中
                addSessionUserMore(className, fieldMap.get(key));
            }
        }
    }
    
    @Override
    public String toString()
    {
        HashMapSO jsonMap = new HashMapSO();
        
        //1.先写入会话标准五属性
        jsonMap.put("sessionId", sessionId);
        jsonMap.put("created", created);
        jsonMap.put("accessed", accessed);
        jsonMap.put("userIp", userIp);
        jsonMap.put("userAgent", userAgent);
        
        //2.再读取会话业务属性，业务属性使用哈希表转数组方式保存为字符串
        List<RmiSessionAttribute> attributeList = new ArrayList<>();
        for (String key : attributeMap.keySet())
        {
            Object val = attributeMap.get(key);
            RmiSessionAttribute attribute = new RmiSessionAttribute();
            attribute.setKey(key);
            attribute.setValue(Jsons.toString(val));
            attribute.setCls(val.getClass().getName());
            if (val instanceof List || val instanceof MapSV)
            {//泛型值
                attribute.setGls(attributeGenerics.get(key));
            }
            
            attributeList.add(attribute);
        }
        jsonMap.put("attributeMap", attributeList);
        
        //3.最后写入会话用户属性
        if (sessionUser != null)
        {
            HashMapSO sUserMap = new HashMapSO();
            
            //3.1 先正常转为JSON，再转为HashMap
            List<Field> fieldList = Classes.getFieldListDeep(sessionUser.getClass());
            for (Field field : fieldList)
            {
                sUserMap.put(field.getName(), Classes.getFieldValue(sessionUser, field));
            }
            
            //3.2 把moreMap加到字段表中
            HashMapSS moreMap = getSessionUserMoreMap();
            for (String key : moreMap.keySet())
            {
                sUserMap.put(key, moreMap.get(key));
            }
            
            //3.3 组装成RmiSessionAttribute数组
            HashMapSO valueMap = sessionUser.getValues();
            
            List<RmiSessionAttribute> valueList = new ArrayList<>();
            for (String key : valueMap.keySet())
            {
                Object val = valueMap.get(key);
                RmiSessionAttribute attribute = new RmiSessionAttribute();
                attribute.setKey(key);
                attribute.setValue(Jsons.toString(val));
                attribute.setCls(val.getClass().getName());
                if (val instanceof List || val instanceof MapSV)
                {//泛型值
                    attribute.setGls(getSessionUserValueGeneric(key));
                }
                
                valueList.add(attribute);
            }
            sUserMap.put("valueMap", new JsonString(Jsons.toString(valueList)));
            
            //3.4 最后生成JSON放置到总的JSON中
            jsonMap.put("sessionUser", sUserMap);
        }
        
        return Jsons.toString(jsonMap);
    }
   
    /**********************************************/
    //SessionUser相关
    /**********************************************/    

    @Override /** 绑定会话用户信息 */
    public boolean bindSessionUser(HttpSessionUser sessionUser)
    {
        boolean val = super.bindSessionUser(sessionUser);
        if (val)
        {//成功的同步到远端
            flush();
        }
        return val;
    }

    @Override /** 解绑会话用户信息 */
    public boolean unbindSessionUser()
    {
        boolean val = super.unbindSessionUser();
        if (val)
        {//成功的同步到远端
            flush();
        }
        
        return val;
    }
    
    /**********************************************/
    //写入关联到session中attribute列表
    /**********************************************/
    
    @Override /** 删除属性 */
    public Object removeAttribute(String name)
    {
        if (!hasAttribute(name))
        {//预先判断一次
            return null;
        }
        
        Object val = super.removeAttribute(name);
        flush();
        return val;
    }
    
    @Override /** 设置属性 */
    public void setAttribute(String name, Object value)
    {
        super.setAttribute(name, value);
        flush();
    }
}
